package kvraft

import (
	"6.5840/labrpc"
	"time"
)
import "crypto/rand"
import "math/big"

const (
	// 重试时间
	RPCRetryTime = time.Millisecond * 50
)

type Clerk struct {
	servers []*labrpc.ClientEnd
	// You will have to modify this struct.
	seq      uint64 // 全局递增的序列号
	clientId int64  // 用来标识clerk
	leaderId int64  // 记录leader
}

func nrand() int64 {
	max := big.NewInt(int64(1) << 62)
	bigx, _ := rand.Int(rand.Reader, max)
	x := bigx.Int64()
	return x
}

func (ck *Clerk) Sequence() uint64 {
	sequence := ck.seq
	ck.seq++
	return sequence
}

func MakeClerk(servers []*labrpc.ClientEnd) *Clerk {
	ck := new(Clerk)
	ck.servers = servers
	// You'll have to add code here.
	ck.clientId = nrand()
	ck.seq = 0

	return ck
}

// fetch the current value for a key.
// returns "" if the key does not exist.
// keeps trying forever in the face of all other errors.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.Get", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
func (ck *Clerk) Get(key string) string {

	// You will have to modify this function.
	args := &GetArgs{
		Key:      key,
		Seq:      ck.Sequence(),
		ClientId: ck.clientId,
	}

	for {
		reply := &GetReply{}
		ok := ck.servers[ck.leaderId].Call("KVServer.Get", args, reply)
		if !ok || reply.Err == ErrWrongLeader || reply.Err == ErrLeaderOutdated {
			DPrintf("clerk %v  Seq %v Retry Get(%s), Err=%s", ck.clientId, ck.seq, args.Key, reply.Err)
			ck.leaderId = (ck.leaderId + 1) % int64(len(ck.servers))
			time.Sleep(RPCRetryTime)
			continue
		} else {
			switch reply.Err {
			case ErrChanClosed:
				DPrintf("clerk %v Seq %v Retry Get(%s), Err=%s)", ck.clientId, ck.seq, args.Key, reply.Err)
				time.Sleep(RPCRetryTime)
				continue
			case ErrHandleOpTimeOut:
				DPrintf("clerk %v Seq %v Retry Get(%s), Err=%s)", ck.clientId, ck.seq, args.Key, reply.Err)
				time.Sleep(RPCRetryTime)
				continue
			case ErrNoKey:
				DPrintf("clerk %v Seq %v Retry Get(%s), Err=%s)", ck.clientId, ck.seq, args.Key, reply.Err)
				return ""
			case OK:
				DPrintf("clerk %v Seq %v Get(%s) successfully and value=%s, Err=%s)", ck.clientId, ck.seq, args.Key, reply.Value, reply.Err)
				return reply.Value
			}
		}
	}
}

// shared by Put and Append.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.PutAppend", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
func (ck *Clerk) PutAppend(key string, value string, op string) {
	// You will have to modify this function.
	args := &PutAppendArgs{
		Key:      key,
		Value:    value,
		Op:       op,
		Seq:      ck.Sequence(),
		ClientId: ck.clientId,
	}

	for {
		reply := &PutAppendReply{}
		ok := ck.servers[ck.leaderId].Call("KVServer.PutAppend", args, reply)
		if !ok || reply.Err == ErrWrongLeader || reply.Err == ErrLeaderOutdated {
			DPrintf("clerk %v  Seq %v Retry %s(%s, %s), Err=%s", ck.clientId, ck.seq, args.Op, args.Key, args.Value, reply.Err)
			ck.leaderId = (ck.leaderId + 1) % int64(len(ck.servers))
			time.Sleep(RPCRetryTime)
			continue
		} else {
			switch reply.Err {
			case ErrChanClosed:
				DPrintf("clerk %v  Seq %v Retry %s(%s, %s), Err=%s", ck.clientId, ck.seq, args.Op, args.Key, args.Value, reply.Err)
				time.Sleep(RPCRetryTime)
				continue
			case ErrHandleOpTimeOut:
				DPrintf("clerk %v  Seq %v Retry %s(%s, %s), Err=%s", ck.clientId, ck.seq, args.Op, args.Key, args.Value, reply.Err)
				time.Sleep(RPCRetryTime)
				continue
			case OK:
				DPrintf("clerk %v  Seq %v %s(%s, %s) successfully, Err=%s", ck.clientId, ck.seq, args.Op, args.Key, args.Value, reply.Err)
				return
			}
		}
	}
}

func (ck *Clerk) Put(key string, value string) {
	ck.PutAppend(key, value, "Put")
}
func (ck *Clerk) Append(key string, value string) {
	ck.PutAppend(key, value, "Append")
}
